Skip to content

Add reconstruction unit tests#128

Open
sebastianrowan wants to merge 19 commits intoUSACE:reconstruction-lifecyclefrom
sebastianrowan:building-reconstruction
Open

Add reconstruction unit tests#128
sebastianrowan wants to merge 19 commits intoUSACE:reconstruction-lifecyclefrom
sebastianrowan:building-reconstruction

Conversation

@sebastianrowan
Copy link
Contributor

@sebastianrowan sebastianrowan commented Jan 29, 2026

This PR adds two functions to the structures module.

  1. computeConsequencesWithReconstruction() calculates the number of days to complete construction on a damaged structure and includes this in the result

    • reconstruction time is treated as a component damage function for a given occtype with percent damage as the xvalue and days to completion as yvalue
    • unit test = TestComputeConsequencesWithReconstruction()
  2. computeConsequencesMulti() calculates the number of days and final date to complete reconstruction for an array of hazard events.

    • unit test = TestComputeConsequencesMulti()

Copy link
Contributor

@HenryGeorgist HenryGeorgist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was great. im asking you to remove files from the commit history that are not directly needed for the tests. i think that there are about 12 files currently included in this, we probably need only about 3 or 4 files total for this to be complete. lets make sure we only are changing those (to minimize our chances for accidentally checking in something we dont want to change)

t2 := time.Date(1984, time.Month(1), 11, 0, 0, 0, 0, time.UTC) // reconstruction from event 1 should be 50% complete at this time
d2.SetArrivalTime(t2)

events := []hazards.HazardEvent{d1, d2}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beautiful

if err != nil {
panic(err)
}
fmt.Printf("Damage was %3.2f. Expected: %3.2f\n", dmgout, expectedDmgs[idx])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfet!

@sebastianrowan
Copy link
Contributor Author

@HenryGeorgist I think I have removed all the edits to files that aren't involved in this PR.

@sebastianrowan
Copy link
Contributor Author

@HenryGeorgist this implementation now keeps track of structure/content values and the overall damage level throughout the series of events. When a new event arrives, we first calculate how much of the total damage (not just from the previous event) has been repaired since the last event ended. Then we calculate loss by applying the damage function to the adjusted structure value. Finally we calculate reconstruction time and completion date based on the total current damage state not just the loss from the current event.


events := []hazards.ArrivalDepthandDurationEvent{d5, d1, d2, d3, d4}

addMulti := &hazards.ArrivalDepthandDurationEventMulti{Events: events} //need to use the pointer reference because methods on MultiHazardEvent require pointers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HenryGeorgist will this be a problem? The Increment() and ResetIndex() methods on the MultiHazardEvent have to use pointers to be able to modify the index value of the struct.

trying to send addMulti to Compute() without the & means it doesn't satisfy the MultiHazardEvent interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either an instance will implement all the methods or the pointer will implement all the methods. So if you have to have a pointer on one then you have to have a pointer on all methods. An alternative: func (h ArrivalDepthAndDurationEvent) Index() ArrivalDepthAndDurationEvent where the interface would have to be Index() MultiHazard

Copy link
Contributor

@HenryGeorgist HenryGeorgist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking great

panic(err)
}
//compute consequences.
StreamAbstract(dfr, nsp, w)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ismt it cool that we get such robustly different behavior out of streamabstract when we leverage the abstractions!

process HazardFunction
}

type ADDProperties struct { // will try to not use this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my first response was why not just use a csv... but json is fine it really doesnt matter. You could also just randomly generate this information or hard code in the list of events for this level of testing we are currenly working on.

"github.com/USACE/go-consequences/hazards"
)

type jsonArrivalDepthDurationMultiHazardProvider struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using cogs at this point is overkill

// We'll probably want to do something different here.
// Probably allow user to define study bbox with a
// shapefile/geojson/directly entering bbox coords
return j.depthCRs[0].GetBoundingBox()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great for now

}

func (h ArrivalDepthandDurationEventMulti) Sort() { // ensure the hazard events are in order of arrival time
sort.Sort(h)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im not sure i like this. i could get comfortable with it. but my first inclination is that we would need to check if the data is not sorted and if it is not reject the dataset and throw an error on the load of the provider (or soemthing) so that the compute doesnt continue with bad data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Maybe there are use cases where it would be beneficial for us to sort the data, but I agree that we should just force users to supply properly formatted datasets. If we change the order of the events silently, the user will not get the output they expect.

Another consideration for future work is that if we are processing multiple series of events generated from a Monte Carlo process as a single input, when the list of events from one lifecycle ends and the next begins, the dates of events will be out of order, and sorting this way would mix up the lifecycles.

if addm.Index() != 0 {
t.Errorf("Index not reset")
}
// check that we can read the Parameters for the events
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great. maybe we could check values since we have the expected array

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is handled on line 162. This was an additional check to confirm the functionality of Has()

return nil
}

type MultiHazardEvent interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it. we may want to step through each intrface and determine its criticality. we may have more overall methods than necessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants